%%%-------------------------------------------------------------------
%%% @author Rain
%%% @copyright (C) 2022, <COMPANY>
%%% @doc
%%% @end
%%%-------------------------------------------------------------------
-module(obj_svr).

-behaviour(gen_server).
-include("obj.hrl").
-include("tower_aoi.hrl").

-export([start_obj_svr/2, stop_obj_svr/1, start_link/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
    code_change/3, get_uid_value/0, set_uid_value/1]).

-define(SERVER, ?MODULE).

-record(state, {uid = #uid{}, sock, player}).

%%%===================================================================
%%% Spawning and gen_server implementation
%%%===================================================================

start_obj_svr(UidValue, Sock) ->
    ChildSpec = #{id => UidValue,
        start => {?MODULE, start_link, [UidValue, Sock]},
        restart => permanent,
        shutdown => 2000,
        type => worker,
        modules => [?MODULE]},
    supervisor:start_child(tower_aoi_server_sup, ChildSpec).

stop_obj_svr(UidValue) ->
    case whereis(?OBJ_SVR(UidValue)) of
        Pid when is_pid(Pid) ->
            Pid ! shutdown;
        _ ->
            pass
    end.

%% @doc 结束进程前调用
do_stop_obj_svr(State = #state{player = #player{uid = Uid = #uid{value = UidValue}}}) ->
    map_util:leave_map(Uid),
    final_do_stop_obj_svr(UidValue),
    State.

%% @doc 最终调用
final_do_stop_obj_svr(UidValue) ->
    Node = node(),
    rpc:cast(Node, supervisor, terminate_child, [tower_aoi_server_sup, UidValue]),
    rpc:cast(Node, supervisor, delete_child, [tower_aoi_server_sup, UidValue]),
    ok.

start_link(UidValue, Sock) ->
    gen_server:start_link({local, ?OBJ_SVR(UidValue)}, ?MODULE, [UidValue, Sock], []).

init([UidValue, Sock]) ->
    erlang:process_flag(trap_exit, true),
    set_uid_value(UidValue),
    {ok, #state{sock = Sock, player = #player{uid = #uid{value = UidValue, type = ?UID_TYPE_PLAYER}}}}.

handle_call(_Request, _From, State = #state{}) ->
    {reply, ok, State}.

handle_cast(_Request, State = #state{}) ->
    {noreply, State}.

handle_info({from_socket_msg, MsgId, Content}, State = #state{player = Player}) ->
    case obj_route:route(MsgId, Content, Player) of
        {noreply, Player1 = #player{}} ->
            {noreply, State#state{player = Player1}};
        {reply, Msg, Player1 = #player{uid = #uid{value = UidValue}}} ->
            gate_util:send_msg(MsgId, Msg, UidValue),
            {noreply, State#state{player = Player1}};
        _ ->
            {noreply, State}
    end;

%% NOTE:由于supervisor的kill超时后会强制清理，有可能会导致terminate的逻辑无法完整执行故把逻辑放于此处
handle_info(shutdown, State = #state{}) ->
    State1 = do_stop_obj_svr(State),
    {noreply, State1};

handle_info(_Info, State = #state{}) ->
    {noreply, State}.

terminate(_Reason, #state{player = #player{uid = #uid{value = UidValue}}} = _State) ->
    io:format("~p [NOTE] OBJ_SVR_~p terminate", [self(), UidValue]),
    ok.

code_change(_OldVsn, State = #state{}, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

get_uid_value() ->
    get(uid_value).

set_uid_value(UidValue) ->
    put(uid_value, UidValue).